1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 *
19 */
20 package org.apache.mina.util;
21
22 import java.security.InvalidParameterException;
23
24 /**
25 * Provides Base64 encoding and decoding as defined by RFC 2045.
26 *
27 * <p>This class implements section <cite>6.8. Base64 Content-Transfer-Encoding</cite>
28 * from RFC 2045 <cite>Multipurpose Internet Mail Extensions (MIME) Part One:
29 * Format of Internet Message Bodies</cite> by Freed and Borenstein.</p>
30 *
31 * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>
32 *
33 * This class was
34 * @author Apache Software Foundation commons codec (http://commons.apache.org/codec/)
35 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
36 */
37 public class Base64 {
38
39 /**
40 * Chunk size per RFC 2045 section 6.8.
41 *
42 * <p>The {@value} character limit does not count the trailing CRLF, but counts
43 * all other characters, including any equal signs.</p>
44 *
45 * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 6.8</a>
46 */
47 static final int CHUNK_SIZE = 76;
48
49 /**
50 * Chunk separator per RFC 2045 section 2.1.
51 *
52 * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 2.1</a>
53 */
54 static final byte[] CHUNK_SEPARATOR = "\r\n".getBytes();
55
56 /**
57 * The base length.
58 */
59 static final int BASELENGTH = 255;
60
61 /**
62 * Lookup length.
63 */
64 static final int LOOKUPLENGTH = 64;
65
66 /**
67 * Used to calculate the number of bits in a byte.
68 */
69 static final int EIGHTBIT = 8;
70
71 /**
72 * Used when encoding something which has fewer than 24 bits.
73 */
74 static final int SIXTEENBIT = 16;
75
76 /**
77 * Used to determine how many bits data contains.
78 */
79 static final int TWENTYFOURBITGROUP = 24;
80
81 /**
82 * Used to get the number of Quadruples.
83 */
84 static final int FOURBYTE = 4;
85
86 /**
87 * Used to test the sign of a byte.
88 */
89 static final int SIGN = -128;
90
91 /**
92 * Byte used to pad output.
93 */
94 static final byte PAD = (byte) '=';
95
96 // Create arrays to hold the base64 characters and a
97 // lookup for base64 chars
98 private static byte[] base64Alphabet = new byte[BASELENGTH];
99
100 private static byte[] lookUpBase64Alphabet = new byte[LOOKUPLENGTH];
101
102 // Populating the lookup and character arrays
103 static {
104 for (int i = 0; i < BASELENGTH; i++) {
105 base64Alphabet[i] = (byte) -1;
106 }
107 for (int i = 'Z'; i >= 'A'; i--) {
108 base64Alphabet[i] = (byte) (i - 'A');
109 }
110 for (int i = 'z'; i >= 'a'; i--) {
111 base64Alphabet[i] = (byte) (i - 'a' + 26);
112 }
113 for (int i = '9'; i >= '0'; i--) {
114 base64Alphabet[i] = (byte) (i - '0' + 52);
115 }
116
117 base64Alphabet['+'] = 62;
118 base64Alphabet['/'] = 63;
119
120 for (int i = 0; i <= 25; i++) {
121 lookUpBase64Alphabet[i] = (byte) ('A' + i);
122 }
123
124 for (int i = 26, j = 0; i <= 51; i++, j++) {
125 lookUpBase64Alphabet[i] = (byte) ('a' + j);
126 }
127
128 for (int i = 52, j = 0; i <= 61; i++, j++) {
129 lookUpBase64Alphabet[i] = (byte) ('0' + j);
130 }
131
132 lookUpBase64Alphabet[62] = (byte) '+';
133 lookUpBase64Alphabet[63] = (byte) '/';
134 }
135
136 private static boolean isBase64(byte octect) {
137 if (octect == PAD) {
138 return true;
139 } else if (base64Alphabet[octect] == -1) {
140 return false;
141 } else {
142 return true;
143 }
144 }
145
146 /**
147 * Tests a given byte array to see if it contains
148 * only valid characters within the Base64 alphabet.
149 *
150 * @param arrayOctect byte array to test
151 * @return true if all bytes are valid characters in the Base64
152 * alphabet or if the byte array is empty; false, otherwise
153 */
154 public static boolean isArrayByteBase64(byte[] arrayOctect) {
155
156 arrayOctect = discardWhitespace(arrayOctect);
157
158 int length = arrayOctect.length;
159 if (length == 0) {
160 // shouldn't a 0 length array be valid base64 data?
161 // return false;
162 return true;
163 }
164 for (int i = 0; i < length; i++) {
165 if (!isBase64(arrayOctect[i])) {
166 return false;
167 }
168 }
169 return true;
170 }
171
172 /**
173 * Encodes binary data using the base64 algorithm but
174 * does not chunk the output.
175 *
176 * @param binaryData binary data to encode
177 * @return Base64 characters
178 */
179 public static byte[] encodeBase64(byte[] binaryData) {
180 return encodeBase64(binaryData, false);
181 }
182
183 /**
184 * Encodes binary data using the base64 algorithm and chunks
185 * the encoded output into 76 character blocks
186 *
187 * @param binaryData binary data to encode
188 * @return Base64 characters chunked in 76 character blocks
189 */
190 public static byte[] encodeBase64Chunked(byte[] binaryData) {
191 return encodeBase64(binaryData, true);
192 }
193
194 /**
195 * Decodes an Object using the base64 algorithm. This method
196 * is provided in order to satisfy the requirements of the
197 * Decoder interface, and will throw a DecoderException if the
198 * supplied object is not of type byte[].
199 *
200 * @param pObject Object to decode
201 * @return An object (of type byte[]) containing the
202 * binary data which corresponds to the byte[] supplied.
203 * @throws InvalidParameterException if the parameter supplied is not
204 * of type byte[]
205 */
206 public Object decode(Object pObject) {
207 if (!(pObject instanceof byte[])) {
208 throw new InvalidParameterException(
209 "Parameter supplied to Base64 decode is not a byte[]");
210 }
211 return decode((byte[]) pObject);
212 }
213
214 /**
215 * Decodes a byte[] containing containing
216 * characters in the Base64 alphabet.
217 *
218 * @param pArray A byte array containing Base64 character data
219 * @return a byte array containing binary data
220 */
221 public byte[] decode(byte[] pArray) {
222 return decodeBase64(pArray);
223 }
224
225 /**
226 * Encodes binary data using the base64 algorithm, optionally
227 * chunking the output into 76 character blocks.
228 *
229 * @param binaryData Array containing binary data to encode.
230 * @param isChunked if isChunked is true this encoder will chunk
231 * the base64 output into 76 character blocks
232 * @return Base64-encoded data.
233 */
234 public static byte[] encodeBase64(byte[] binaryData, boolean isChunked) {
235 int lengthDataBits = binaryData.length * EIGHTBIT;
236 int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
237 int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
238 byte encodedData[] = null;
239 int encodedDataLength = 0;
240 int nbrChunks = 0;
241
242 if (fewerThan24bits != 0) {
243 //data not divisible by 24 bit
244 encodedDataLength = (numberTriplets + 1) * 4;
245 } else {
246 // 16 or 8 bit
247 encodedDataLength = numberTriplets * 4;
248 }
249
250 // If the output is to be "chunked" into 76 character sections,
251 // for compliance with RFC 2045 MIME, then it is important to
252 // allow for extra length to account for the separator(s)
253 if (isChunked) {
254
255 nbrChunks = (CHUNK_SEPARATOR.length == 0 ? 0 : (int) Math
256 .ceil((float) encodedDataLength / CHUNK_SIZE));
257 encodedDataLength += nbrChunks * CHUNK_SEPARATOR.length;
258 }
259
260 encodedData = new byte[encodedDataLength];
261
262 byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
263
264 int encodedIndex = 0;
265 int dataIndex = 0;
266 int i = 0;
267 int nextSeparatorIndex = CHUNK_SIZE;
268 int chunksSoFar = 0;
269
270 //log.debug("number of triplets = " + numberTriplets);
271 for (i = 0; i < numberTriplets; i++) {
272 dataIndex = i * 3;
273 b1 = binaryData[dataIndex];
274 b2 = binaryData[dataIndex + 1];
275 b3 = binaryData[dataIndex + 2];
276
277 //log.debug("b1= " + b1 +", b2= " + b2 + ", b3= " + b3);
278
279 l = (byte) (b2 & 0x0f);
280 k = (byte) (b1 & 0x03);
281
282 byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
283 : (byte) ((b1) >> 2 ^ 0xc0);
284 byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4)
285 : (byte) ((b2) >> 4 ^ 0xf0);
286 byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6)
287 : (byte) ((b3) >> 6 ^ 0xfc);
288
289 encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
290 //log.debug( "val2 = " + val2 );
291 //log.debug( "k4 = " + (k<<4) );
292 //log.debug( "vak = " + (val2 | (k<<4)) );
293 encodedData[encodedIndex + 1] = lookUpBase64Alphabet[val2
294 | (k << 4)];
295 encodedData[encodedIndex + 2] = lookUpBase64Alphabet[(l << 2)
296 | val3];
297 encodedData[encodedIndex + 3] = lookUpBase64Alphabet[b3 & 0x3f];
298
299 encodedIndex += 4;
300
301 // If we are chunking, let's put a chunk separator down.
302 if (isChunked) {
303 // this assumes that CHUNK_SIZE % 4 == 0
304 if (encodedIndex == nextSeparatorIndex) {
305 System.arraycopy(CHUNK_SEPARATOR, 0, encodedData,
306 encodedIndex, CHUNK_SEPARATOR.length);
307 chunksSoFar++;
308 nextSeparatorIndex = (CHUNK_SIZE * (chunksSoFar + 1))
309 + (chunksSoFar * CHUNK_SEPARATOR.length);
310 encodedIndex += CHUNK_SEPARATOR.length;
311 }
312 }
313 }
314
315 // form integral number of 6-bit groups
316 dataIndex = i * 3;
317
318 if (fewerThan24bits == EIGHTBIT) {
319 b1 = binaryData[dataIndex];
320 k = (byte) (b1 & 0x03);
321 //log.debug("b1=" + b1);
322 //log.debug("b1<<2 = " + (b1>>2) );
323 byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
324 : (byte) ((b1) >> 2 ^ 0xc0);
325 encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
326 encodedData[encodedIndex + 1] = lookUpBase64Alphabet[k << 4];
327 encodedData[encodedIndex + 2] = PAD;
328 encodedData[encodedIndex + 3] = PAD;
329 } else if (fewerThan24bits == SIXTEENBIT) {
330
331 b1 = binaryData[dataIndex];
332 b2 = binaryData[dataIndex + 1];
333 l = (byte) (b2 & 0x0f);
334 k = (byte) (b1 & 0x03);
335
336 byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
337 : (byte) ((b1) >> 2 ^ 0xc0);
338 byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4)
339 : (byte) ((b2) >> 4 ^ 0xf0);
340
341 encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
342 encodedData[encodedIndex + 1] = lookUpBase64Alphabet[val2
343 | (k << 4)];
344 encodedData[encodedIndex + 2] = lookUpBase64Alphabet[l << 2];
345 encodedData[encodedIndex + 3] = PAD;
346 }
347
348 if (isChunked) {
349 // we also add a separator to the end of the final chunk.
350 if (chunksSoFar < nbrChunks) {
351 System.arraycopy(CHUNK_SEPARATOR, 0, encodedData,
352 encodedDataLength - CHUNK_SEPARATOR.length,
353 CHUNK_SEPARATOR.length);
354 }
355 }
356
357 return encodedData;
358 }
359
360 /**
361 * Decodes Base64 data into octects
362 *
363 * @param base64Data Byte array containing Base64 data
364 * @return Array containing decoded data.
365 */
366 public static byte[] decodeBase64(byte[] base64Data) {
367 // RFC 2045 requires that we discard ALL non-Base64 characters
368 base64Data = discardNonBase64(base64Data);
369
370 // handle the edge case, so we don't have to worry about it later
371 if (base64Data.length == 0) {
372 return new byte[0];
373 }
374
375 int numberQuadruple = base64Data.length / FOURBYTE;
376 byte decodedData[] = null;
377 byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0;
378
379 // Throw away anything not in base64Data
380
381 int encodedIndex = 0;
382 int dataIndex = 0;
383 {
384 // this sizes the output array properly - rlw
385 int lastData = base64Data.length;
386 // ignore the '=' padding
387 while (base64Data[lastData - 1] == PAD) {
388 if (--lastData == 0) {
389 return new byte[0];
390 }
391 }
392 decodedData = new byte[lastData - numberQuadruple];
393 }
394
395 for (int i = 0; i < numberQuadruple; i++) {
396 dataIndex = i * 4;
397 marker0 = base64Data[dataIndex + 2];
398 marker1 = base64Data[dataIndex + 3];
399
400 b1 = base64Alphabet[base64Data[dataIndex]];
401 b2 = base64Alphabet[base64Data[dataIndex + 1]];
402
403 if (marker0 != PAD && marker1 != PAD) {
404 //No PAD e.g 3cQl
405 b3 = base64Alphabet[marker0];
406 b4 = base64Alphabet[marker1];
407
408 decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
409 decodedData[encodedIndex + 1] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
410 decodedData[encodedIndex + 2] = (byte) (b3 << 6 | b4);
411 } else if (marker0 == PAD) {
412 //Two PAD e.g. 3c[Pad][Pad]
413 decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
414 } else if (marker1 == PAD) {
415 //One PAD e.g. 3cQ[Pad]
416 b3 = base64Alphabet[marker0];
417
418 decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
419 decodedData[encodedIndex + 1] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
420 }
421 encodedIndex += 3;
422 }
423 return decodedData;
424 }
425
426 /**
427 * Discards any whitespace from a base-64 encoded block.
428 *
429 * @param data The base-64 encoded data to discard the whitespace
430 * from.
431 * @return The data, less whitespace (see RFC 2045).
432 */
433 static byte[] discardWhitespace(byte[] data) {
434 byte groomedData[] = new byte[data.length];
435 int bytesCopied = 0;
436
437 for (int i = 0; i < data.length; i++) {
438 switch (data[i]) {
439 case (byte) ' ':
440 case (byte) '\n':
441 case (byte) '\r':
442 case (byte) '\t':
443 break;
444 default:
445 groomedData[bytesCopied++] = data[i];
446 }
447 }
448
449 byte packedData[] = new byte[bytesCopied];
450
451 System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
452
453 return packedData;
454 }
455
456 /**
457 * Discards any characters outside of the base64 alphabet, per
458 * the requirements on page 25 of RFC 2045 - "Any characters
459 * outside of the base64 alphabet are to be ignored in base64
460 * encoded data."
461 *
462 * @param data The base-64 encoded data to groom
463 * @return The data, less non-base64 characters (see RFC 2045).
464 */
465 static byte[] discardNonBase64(byte[] data) {
466 byte groomedData[] = new byte[data.length];
467 int bytesCopied = 0;
468
469 for (int i = 0; i < data.length; i++) {
470 if (isBase64(data[i])) {
471 groomedData[bytesCopied++] = data[i];
472 }
473 }
474
475 byte packedData[] = new byte[bytesCopied];
476
477 System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
478
479 return packedData;
480 }
481
482 // Implementation of the Encoder Interface
483
484 /**
485 * Encodes an Object using the base64 algorithm. This method
486 * is provided in order to satisfy the requirements of the
487 * Encoder interface, and will throw an EncoderException if the
488 * supplied object is not of type byte[].
489 *
490 * @param pObject Object to encode
491 * @return An object (of type byte[]) containing the
492 * base64 encoded data which corresponds to the byte[] supplied.
493 * @throws InvalidParameterException if the parameter supplied is not
494 * of type byte[]
495 */
496 public Object encode(Object pObject) {
497 if (!(pObject instanceof byte[])) {
498 throw new InvalidParameterException(
499 "Parameter supplied to Base64 encode is not a byte[]");
500 }
501 return encode((byte[]) pObject);
502 }
503
504 /**
505 * Encodes a byte[] containing binary data, into a byte[] containing
506 * characters in the Base64 alphabet.
507 *
508 * @param pArray a byte array containing binary data
509 * @return A byte array containing only Base64 character data
510 */
511 public byte[] encode(byte[] pArray) {
512 return encodeBase64(pArray, false);
513 }
514
515 }